/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// cl_ents.c -- entity parsing and management

#include "cl_local.h"
#include "client.h"

extern	struct model_t	*cl_mod_powerscreen;

/*
=========================================================================

FRAME PARSING

=========================================================================
*/

/*
=================
CL_ParseEntityBits

Returns the entity number and the header bits
=================
*/
//int	bitcounts[32];	/// just for protocol profiling
int CL_ParseEntityBits (sizebuf_t *msg, unsigned int *bits)
{
	unsigned int b, total, number;

	total = MSG_ReadByte (msg);
	if (total & U_MOREBITS1)
	{
		b = MSG_ReadByte (msg);
		total |= b<<8;
	}
	if (total & U_MOREBITS2)
	{
		b = MSG_ReadByte (msg);
		total |= b<<16;
	}
	if (total & U_MOREBITS3)
	{
		b = MSG_ReadByte (msg);
		total |= b<<24;
	}

	// count the bits for net profiling
	/*for (i=0 ; i<32 ; i++)
		if (total&(1<<i))
			bitcounts[i]++;*/

	if (total & U_NUMBER16)
	{
		number = MSG_ReadShort (msg);
		if (number >= MAX_EDICTS)
			Com_Error (ERR_DROP, "CL_ParseEntityBits: Bad entity number %u", number);
	}
	else
	{
		number = MSG_ReadByte (msg);
	}

	*bits = total;

	return (int)number;
}

/*
==================
CL_DeltaEntity

Parses deltas from the given base and adds the resulting entity
to the current frame
==================
*/
static void CL_DeltaEntity (sizebuf_t *msg, frame_t *frame, int newnum, const entity_state_t *old, int bits)
{
	centity_t	*ent;
	entity_state_t	*state;

	ent = &cl.entities[newnum];

	state = &cl.parseEntities[cl.parse_entities & PARSE_ENTITIES_MASK];
	cl.parse_entities++;
	frame->numEntities++;

	MSG_ParseDeltaEntity(msg, old, state, newnum, bits, cls2.serverProtocol);

	// some data changes will force no lerping
	if (state->modelindex != ent->current.modelindex
		|| state->modelindex2 != ent->current.modelindex2
		|| state->modelindex3 != ent->current.modelindex3
		|| state->modelindex4 != ent->current.modelindex4
		|| abs(state->origin[0] - ent->current.origin[0]) > 512
		|| abs(state->origin[1] - ent->current.origin[1]) > 512
		|| abs(state->origin[2] - ent->current.origin[2]) > 512
		|| state->event == EV_PLAYER_TELEPORT
		|| state->event == EV_OTHER_TELEPORT
		)
	{
		ent->serverframe = -99;
	}

	if (ent->serverframe != cl.frame.serverframe - 1)
	{	// wasn't in last update, so initialize some things
		ent->trailcount = 1024;		// for diminishing rocket / grenade trails
		// duplicate the current state so lerping doesn't hurt anything
		ent->prev = *state;
		if (state->event == EV_OTHER_TELEPORT)
		{
			VectorCopy (state->origin, ent->prev.origin);
			VectorCopy (state->origin, ent->lerpOrigin);
		}
		else
		{
			VectorCopy (state->old_origin, ent->prev.origin);
			VectorCopy (state->old_origin, ent->lerpOrigin);
		}
	}
	else
	{	// shuffle the last state to previous
		ent->prev = ent->current;
	}

	ent->serverframe = cl.frame.serverframe;
	ent->current = *state;
}

/*
==================
CL_ParsePacketEntities

An svc_packetentities has just been parsed, deal with the
rest of the data stream.
==================
*/
static void CL_ParsePacketEntities (sizebuf_t *msg, const frame_t *oldframe, frame_t *newframe)
{
	int			newnum;
	unsigned int	bits;
	entity_state_t	*oldstate  = NULL;
	int			oldindex = 0, oldnum;

	newframe->parseEntitiesIndex = cl.parse_entities;
	newframe->numEntities = 0;

	// delta from the entities present in oldframe
	if (!oldframe)
		oldnum = 99999;
	else
	{
		if (oldindex >= oldframe->numEntities)
			oldnum = 99999;
		else
		{
			oldstate = &cl.parseEntities[(oldframe->parseEntitiesIndex+oldindex) & PARSE_ENTITIES_MASK];
			oldnum = oldstate->number;
		}
	}

	while (1)
	{
		newnum = CL_ParseEntityBits(msg, &bits);

		if (msg->readcount > msg->cursize)
			Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");

		if (!newnum)
			break;

		while (oldnum < newnum)
		{	// one or more entities from the old packet are unchanged
			if (cl_shownet->integer == 3)
				Com_Printf ("   unchanged: %i\n", oldnum);
			CL_DeltaEntity(msg, newframe, oldnum, oldstate, 0);
			
			oldindex++;

			if (oldindex >= oldframe->numEntities)
				oldnum = 99999;
			else
			{
				oldstate = &cl.parseEntities[(oldframe->parseEntitiesIndex+oldindex) & PARSE_ENTITIES_MASK];
				oldnum = oldstate->number;
			}
		}

		if (bits & U_REMOVE)
		{	// the entity present in oldframe is not in the current frame
			if (cl_shownet->integer == 3)
				Com_Printf ("   remove: %i\n", newnum);
			if (oldnum != newnum)
				Com_DPrintf ("U_REMOVE: oldnum != newnum\n");

			oldindex++;

			if (oldindex >= oldframe->numEntities)
				oldnum = 99999;
			else
			{
				oldstate = &cl.parseEntities[(oldframe->parseEntitiesIndex+oldindex) & PARSE_ENTITIES_MASK];
				oldnum = oldstate->number;
			}
			continue;
		}

		if (oldnum == newnum)
		{	// delta from previous state
			if (cl_shownet->integer == 3)
				Com_Printf ("   delta: %i\n", newnum);
			CL_DeltaEntity(msg, newframe, newnum, oldstate, bits);

			oldindex++;

			if (oldindex >= oldframe->numEntities)
				oldnum = 99999;
			else
			{
				oldstate = &cl.parseEntities[(oldframe->parseEntitiesIndex+oldindex) & PARSE_ENTITIES_MASK];
				oldnum = oldstate->number;
			}
			continue;
		}

		if (oldnum > newnum)
		{	// delta from baseline
			if (cl_shownet->integer == 3)
				Com_Printf ("   baseline: %i\n", newnum);
			CL_DeltaEntity(msg, newframe, newnum, &cl.entities[newnum].baseline, bits);
			continue;
		}

	}

	// any remaining entities in the old frame are copied over
	while (oldnum != 99999)
	{	// one or more entities from the old packet are unchanged
		if (cl_shownet->integer == 3)
			Com_Printf ("   unchanged: %i\n", oldnum);
		CL_DeltaEntity(msg, newframe, oldnum, oldstate, 0);
		
		oldindex++;

		if (oldindex >= oldframe->numEntities)
			oldnum = 99999;
		else
		{
			oldstate = &cl.parseEntities[(oldframe->parseEntitiesIndex+oldindex) & PARSE_ENTITIES_MASK];
			oldnum = oldstate->number;
		}
	}
}



//r1: this fakes a protocol 34 packetentites write from the clients state instead
//of the server. used to write demo stream regardless of c/s protocol in use.
static void CL_DemoPacketEntities (sizebuf_t *buf, const frame_t /*@null@*/*from, const frame_t *to)
{
	const entity_state_t	*oldent = NULL;
	const entity_state_t	*newent = NULL;

	int				oldindex = 0, newindex = 0;
	int				oldnum, newnum;
	int				from_num_entities;

	//r1: pointless waste of byte since this is already inside an svc_frame
	MSG_WriteByte(buf, svc_packetentities);

	if (!from)
		from_num_entities = 0;
	else
		from_num_entities = from->numEntities;

	while (newindex < to->numEntities || oldindex < from_num_entities)
	{
		if (newindex >= to->numEntities)
			newnum = 9999;
		else
		{
			newent = &cl.parseEntities[(to->parseEntitiesIndex +newindex) & PARSE_ENTITIES_MASK];
			newnum = newent->number;
		}

		if (oldindex >= from_num_entities)
			oldnum = 9999;
		else
		{
			//Com_Printf ("server: its in old entities!\n");
			oldent = &cl.parseEntities[(from->parseEntitiesIndex+oldindex) & PARSE_ENTITIES_MASK];
			oldnum = oldent->number;
		}

		if (newnum == oldnum)
		{	// delta update from old position
			// because the force parm is false, this will not result
			// in any bytes being emited if the entity has not changed at all
			// note that players are always 'newentities', this updates their oldorigin always
			// and prevents warping

			MSG_WriteDeltaEntity(oldent, newent, buf, false, newent->number <= cl.maxclients);

			oldindex++;
			newindex++;
			continue;
		}
	
		if (newnum < oldnum)
		{	// this is a new entity, send it from the baseline
			MSG_WriteDeltaEntity(&cl.entities[newnum].baseline, newent, buf, true, true);
			newindex++;
			continue;
		}

		if (newnum > oldnum)
		{	// the old entity isn't present in the new message
			MSG_WriteDeltaEntity(oldent, NULL, buf, true, false);
			oldindex++;
			continue;
		}
	}

	MSG_WriteShort (buf, 0);	// end of packetentities
}


/*
==================
CL_FireEntityEvents

==================
*/
void CL_FireEntityEvents (const frame_t *frame)
{
	entity_state_t		*s1;
	int					pnum, num;

	for (pnum = 0 ; pnum<frame->numEntities ; pnum++)
	{
		num = (frame->parseEntitiesIndex + pnum) & PARSE_ENTITIES_MASK;
		s1 = &cl.parseEntities[num];
		//if (s1->event)
			//CL_EntityEvent (s1);

		// EF_TELEPORTER acts like an event, but is not cleared each frame
		//if (s1->effects & EF_TELEPORTER)
		//	CL_TeleporterParticles (s1);
	}
}

void CL_EntityEvent ();


/*
================
CL_ParseFrame
================
*/
void CL_ParseFrame (sizebuf_t *msg, int extrabits)
{	
	int			cmd, len;
	uint32		bits, extraflags = 0;
	frame_t		*oldframe;
	player_state_t	*from;

	memset (&cl.frame, 0, sizeof(cl.frame));

	if (cls2.serverProtocol > PROTOCOL_VERSION_DEFAULT)
	{	
		bits = MSG_ReadLong(msg);

		cl.frame.serverframe = bits & FRAMENUM_MASK;
		bits >>= FRAMENUM_BITS;
		
		if (bits == 31)
			cl.frame.deltaframe = -1;
		else
			cl.frame.deltaframe = cl.frame.serverframe - bits;

		bits = MSG_ReadByte(msg);

		cl.surpressCount = bits & SURPRESSCOUNT_MASK;
		extraflags = ( extrabits << 4 ) | ( bits >> SURPRESSCOUNT_BITS );
	}
	else
	{
		cl.frame.serverframe = MSG_ReadLong(msg);
		cl.frame.deltaframe = MSG_ReadLong(msg);

		/* BIG HACK to let old demos continue to work */
		if (cls2.serverProtocol != 26)
			cl.surpressCount = MSG_ReadByte(msg);
	}

	cl.serverTime = cl.frame.serverframe*100;

	if (cl_shownet->integer == 3)
		Com_Printf ("   frame:%i  delta:%i\n", cl.frame.serverframe, cl.frame.deltaframe);

	// If the frame is delta compressed from data that we
	// no longer have available, we must suck up the rest of
	// the frame, but not use it, then ask for a non-compressed
	// message 
	if (cl.frame.deltaframe > 0)
	{
		oldframe = &cl.frames[cl.frame.deltaframe & UPDATE_MASK];
		from = &oldframe->playerstate;
		if( cl.frame.serverframe == cl.frame.deltaframe ) {
            /* old buggy q2 servers still cause this on map change */
			Com_DPrintf( "Delta from current frame (should not happen).\n" );
		}
		else if (!oldframe->valid)
		{	// should never happen
			Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
		}
		if (oldframe->serverframe != cl.frame.deltaframe)
		{	// The frame that the server did the delta from
			// is too old, so we can't reconstruct it properly.
			Com_DPrintf ("Delta frame too old.\n");
		}
		else if (cl.parse_entities - oldframe->parseEntitiesIndex > MAX_PARSE_ENTITIES-128)
		{
			Com_DPrintf ("Delta parse_entities too old.\n");
		}
		else
			cl.frame.valid = true;	// valid delta parse
	}
	else
	{
		cl.frame.valid = true;		// uncompressed frame
		oldframe = NULL;
		cls2.demowaiting = false;	// we can start recording now
		from = NULL;
	}

	// clamp time 
	if (cl.time > cl.serverTime)
		cl.time = cl.serverTime;
	else if (cl.time < cl.serverTime - 100)
		cl.time = cl.serverTime - 100;

	// read areabits
	len = MSG_ReadByte(msg);
	if (len) {
		if (len > sizeof(cl.frame.areabits)) {
			Com_Error(ERR_DROP, "CL_ParseFrame: invalid areabits length");
		}
		if (msg->readcount + len > msg->cursize) {
			Com_Error(ERR_DROP, "CL_ParseFrame: read past end of message");
		}
		MSG_ReadData(msg, cl.frame.areabits, len);
	}

	if( cls2.serverProtocol > PROTOCOL_VERSION_DEFAULT ) {
		/* parse playerstate */
		bits = MSG_ReadShort(msg);
		MSG_ParseDeltaPlayerstate_Enhanced( msg, from, &cl.frame.playerstate, bits, extraflags );

		CL_ParsePacketEntities(msg, oldframe, &cl.frame);

		//r1: now write protocol 34 compatible delta from our localstate for demo.
		if (cls2.demorecording)
		{
			sizebuf_t	fakeMsg;
			byte		fakeDemoFrame[1300];
			player_state_t		*oldstate;

			//do it
			SZ_Init (&fakeMsg, fakeDemoFrame, sizeof(fakeDemoFrame));
			fakeMsg.allowoverflow = true;

			//svc_frame header shit
			MSG_WriteByte(&fakeMsg, svc_frame);
			MSG_WriteLong(&fakeMsg, cl.frame.serverframe);
			MSG_WriteLong(&fakeMsg, cl.frame.deltaframe);
			MSG_WriteByte(&fakeMsg, cl.surpressCount);

			//areabits
			MSG_WriteByte (&fakeMsg, len);
			SZ_Write(&fakeMsg, &cl.frame.areabits, len);

			//delta ps
			MSG_WriteByte(&fakeMsg, svc_playerinfo);
			if( oldframe ) {
				oldstate = &oldframe->playerstate;	
			} else {
				oldstate = NULL;
			}
			MSG_WriteDeltaPlayerstate_Default(oldstate, &cl.frame.playerstate, &fakeMsg);

			//delta pe
			CL_DemoPacketEntities(&fakeMsg, oldframe, &cl.frame);

			//copy to demobuff
			if (!fakeMsg.overflowed)
			{
				if (fakeMsg.cursize + cl.demoBuff.cursize > cl.demoBuff.maxsize)
					Com_DPrintf ("Discarded a demoframe of %d bytes.\n", fakeMsg.cursize);
				else
					SZ_Write (&cl.demoBuff, fakeDemoFrame, fakeMsg.cursize);
			}
		}
	} else {

		// read playerinfo
		cmd = MSG_ReadByte(msg);
		if (cmd != svc_playerinfo)
			Com_Error (ERR_DROP, "CL_ParseFrame: 0x%.2x not playerinfo", cmd);

		SHOWNET(msg, svc_strings[cmd]);

		/* parse playerstate */
		bits = MSG_ReadShort(msg);
		MSG_ParseDeltaPlayerstate_Default( msg, from, &cl.frame.playerstate, bits );

		// read packet entities
		cmd = MSG_ReadByte(msg);
		if (cmd != svc_packetentities)
			Com_Error (ERR_DROP, "CL_ParseFrame: 0x%.2x not packetentities", cmd);
		SHOWNET(msg, svc_strings[cmd]);

		CL_ParsePacketEntities(msg, oldframe, &cl.frame);
	}

	// save the frame off in the backup array for later delta comparisons
	cl.frames[cl.frame.serverframe & UPDATE_MASK] = cl.frame;

	if (!cl.frame.valid)
		return;

	// getting a valid frame message ends the connection process
	if (cls2.state != ca_active)
	{
		cls2.state = ca_active;

		cls.state = CS_ACTIVE;

		//cl.time = cl.serverTime;
		//cl.initial_server_frame = cl.frame.serverframe;
		//cl.frame.servertime = (cl.frame.serverframe - cl.initial_server_frame) * 100;

		cl.force_refdef = true;
		VectorScale(cl.frame.playerstate.pmove.origin, 0.125f, cl.predicted_origin);
		VectorCopy (cl.frame.playerstate.viewangles, cl.predicted_angles);

		//SCR_ClearLagometer();
		//SCR_ClearChatHUD_f();

		if (cls2.disable_servercount != cl.servercount && cl.refresh_prepped)
			SCR_EndLoadingPlaque ();	// get rid of loading plaque

		if(!cl.attractloop) {
			Cmd_ExecTrigger( "#cl_enterlevel" );
			//CL_StartAutoRecord();
		}

		cl.sound_prepped = true;	// can start mixing ambient sounds
	}

	// fire entity events
	CL_RunLightStyles();
	CL_BuildSolidList();
	CL_EntityEvent();
	CL_CheckPredictionError();
}

/*
==========================================================================

INTERPOLATE BETWEEN FRAMES TO GET RENDERING PARMS

==========================================================================
*/



extern int Developer_searchpath( void );


/*
===============
CL_CalcViewValues2

Sets cl.refdef view values
===============
*/
void CL_CalcViewValues2 (void)
{
	int			i;
	float		lerp, backlerp;
	frame_t		*oldframe;
	qboolean demoplayback;

	// find the previous frame to interpolate from
	cl.playerState = &cl.frame.playerstate;
	i = (cl.frame.serverframe - 1) & UPDATE_MASK;
	oldframe = &cl.frames[i];
	if (oldframe->serverframe != cl.frame.serverframe-1 || !oldframe->valid)
		oldframe = &cl.frame;		// previous frame was dropped or involid
	cl.oldPlayerState = &oldframe->playerstate;

	// see if the player entity was teleported this frame
	if ( fabsf(cl.oldPlayerState->pmove.origin[0] - cl.playerState->pmove.origin[0]) > 2048
		|| abs(cl.oldPlayerState->pmove.origin[1] - cl.playerState->pmove.origin[1]) > 2048
		|| abs(cl.oldPlayerState->pmove.origin[2] - cl.playerState->pmove.origin[2]) > 2048)
		cl.oldPlayerState = cl.playerState;		// don't interpolate

	//ent = &cl.entities[cl.playernum+1];
	lerp = cl.lerpFrac;

	demoplayback = (Com_ServerState() == ss_demo || cls2.demoplaying);
	// calculate the origin
	if (!demoplayback && cl_predict->integerValue && !(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))
	{	// use predicted values
		unsigned	delta;

		backlerp = 1.0f - lerp;
		for (i=0 ; i<3 ; i++)
		{
			cl.renderView.viewOrigin[i] = cl.predicted_origin[i] + cl.oldPlayerState->viewoffset[i]
				+ lerp * (cl.playerState->viewoffset[i] - cl.oldPlayerState->viewoffset[i])
				- backlerp * cl.prediction_error[i];
		}

		// smooth out stair climbing
		delta = cls2.realtime - cl.predicted_step_time;
		if (delta < 100)
			cl.renderView.viewOrigin[2] -= cl.predicted_step * (100 - delta) * 0.01f;
	}
	else
	{	// just use interpolated values
		for (i=0 ; i<3 ; i++)
			cl.renderView.viewOrigin[i] = cl.oldPlayerState->pmove.origin[i]*0.125f + cl.oldPlayerState->viewoffset[i] 
				+ lerp * (cl.playerState->pmove.origin[i]*0.125f + cl.playerState->viewoffset[i] 
				- (cl.oldPlayerState->pmove.origin[i]*0.125f + cl.oldPlayerState->viewoffset[i]) );
	}

	// if not running a demo or on a locked frame, add the local angle movement
	if (demoplayback)
	{
#if 0
		if( Key_IsDown( K_SHIFT ) ) {
			VectorCopy(cl.predicted_angles, cl.renderView.viewangles);
		} else {
			cl.renderView.viewangles[0] = LerpAngle (cl.oldPlayerState->viewangles[0], cl.playerState->viewangles[0], lerp);
			cl.renderView.viewangles[1] = LerpAngle (cl.oldPlayerState->viewangles[1], cl.playerState->viewangles[1], lerp);
			cl.renderView.viewangles[2] = LerpAngle (cl.oldPlayerState->viewangles[2], cl.playerState->viewangles[2], lerp);
		}
#endif
	}
	else if (cl.playerState->pmove.pm_type < PM_DEAD)
	{	// use predicted values
		VectorCopy (cl.predicted_angles, cl.renderView.viewangles);
	}
	else if (cl.oldPlayerState->pmove.pm_type < PM_DEAD && cls2.serverProtocol > PROTOCOL_VERSION_DEFAULT)
	{	//r1: fix for server no longer sending viewangles every frame.
		cl.renderView.viewangles[0] = LerpAngle (cl.predicted_angles[0], cl.playerState->viewangles[0], lerp);
		cl.renderView.viewangles[1] = LerpAngle (cl.predicted_angles[1], cl.playerState->viewangles[1], lerp);
		cl.renderView.viewangles[2] = LerpAngle (cl.predicted_angles[2], cl.playerState->viewangles[2], lerp);
	}
	else
	{	// just use interpolated values
		cl.renderView.viewangles[0] = LerpAngle (cl.oldPlayerState->viewangles[0], cl.playerState->viewangles[0], lerp);
		cl.renderView.viewangles[1] = LerpAngle (cl.oldPlayerState->viewangles[1], cl.playerState->viewangles[1], lerp);
		cl.renderView.viewangles[2] = LerpAngle (cl.oldPlayerState->viewangles[2], cl.playerState->viewangles[2], lerp);
	}

	cl.renderView.viewangles[0] += LerpAngle (cl.oldPlayerState->kick_angles[0], cl.playerState->kick_angles[0], lerp);
	cl.renderView.viewangles[1] += LerpAngle (cl.oldPlayerState->kick_angles[1], cl.playerState->kick_angles[1], lerp);
	cl.renderView.viewangles[2] += LerpAngle (cl.oldPlayerState->kick_angles[2], cl.playerState->kick_angles[2], lerp);

	AngleVectors (cl.renderView.viewangles, cl.v_forward, cl.v_right, cl.v_up);

	AnglesToAxis(cl.renderView.viewangles, cl.renderView.viewAxis);

	// interpolate field of view
	cl.renderView.fovX = cl.oldPlayerState->fov + lerp * (cl.playerState->fov - cl.oldPlayerState->fov);

	// don't interpolate blend color
	Vector4Copy (cl.playerState->blend, cl.renderView.blend);

	// add the weapon
	CL_AddViewWeapon ();
}

void CL_AddPacketEntities ();

/*
===============
CL_AddEntities

Emits all entities, particles, and lights to the refresh
===============
*/
void CL_AddEntities (void)
{

	if (cl.time > cl.serverTime)
	{
		if (cl_showclamp->integer)
			Com_Printf ("high clamp %i\n", cl.time - cl.serverTime);
		cl.time = cl.serverTime;
		cl.lerpFrac = 1.0f;
	}
	else if (cl.time < cl.serverTime - 100)
	{
		if (cl_showclamp->integer)
			Com_Printf ("low clamp %i\n", cl.serverTime-100 - cl.time);
		cl.time = cl.serverTime - 100;
		cl.lerpFrac = 0;
	}
	else
		cl.lerpFrac = 1.0f - (cl.serverTime - cl.time) * 0.01f;

	if (cl_timedemo->integer)
		cl.lerpFrac = 1.0f;

	CL_CalcViewValues2 ();
	// PMM - moved this here so the heat beam has the right values for the viewOrigin, and can lock the beam to the gun
	CL_AddPacketEntities();

	CL_AddViewLocs();

	//CL_AddTEnts ();
	//CL_AddParticles2 ();
	//CL_AddDLights ();
	//CL_AddLightStyles ();
}


/*
===============
CL_GetEntitySoundOrigin

Called to get the sound spatialization origin
===============
*/
void CL_GetEntitySoundOrigin (int ent, vec3_t org)
{
	centity_t	*cent;

	if ((unsigned)ent >= MAX_EDICTS)
		Com_Error(ERR_DROP, "CL_GetEntityOrigin: ent = %i", ent);

	// Player entity
	if (ent == cl.playernum + 1)
	{
		VectorCopy (cl.renderView.viewOrigin, org);
		return;
	}

	cent = &cl.entities[ent];
	//VectorCopy(cent->lerp_origin, org);

	if (cent->current.renderfx & (RF_FRAMELERP|RF_BEAM))
	{
		// Calculate origin
		org[0] = cent->current.old_origin[0] + (cent->current.origin[0] - cent->current.old_origin[0]) * cl.lerpFrac;
		org[1] = cent->current.old_origin[1] + (cent->current.origin[1] - cent->current.old_origin[1]) * cl.lerpFrac;
		org[2] = cent->current.old_origin[2] + (cent->current.origin[2] - cent->current.old_origin[2]) * cl.lerpFrac;
	}
	else
	{
		// Calculate origin
		org[0] = cent->prev.origin[0] + (cent->current.origin[0] - cent->prev.origin[0]) * cl.lerpFrac;
		org[1] = cent->prev.origin[1] + (cent->current.origin[1] - cent->prev.origin[1]) * cl.lerpFrac;
		org[2] = cent->prev.origin[2] + (cent->current.origin[2] - cent->prev.origin[2]) * cl.lerpFrac;
	}

	// If a brush model, offset the origin
	if (cent->current.solid == 31)
	{
		vec3_t		midPoint;
		cmodel_t	*cmodel = cl.media.gameCModels[cent->current.modelindex];

		if (!cmodel)
			return;

		VectorAvg(cmodel->mins, cmodel->maxs, midPoint);
		VectorAdd(org, midPoint, org);
	}
}

